#include <stdio.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>


//监控多个文件描述符
//0：可读事件
//tcp侦听套接字：可读事件
//
//验证：select在返回的时候，只会返回就绪的文件描述符


int main()
{
    //创建侦听套接字
    int listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(listen_sock < 0)
    {
        perror("socket");
        return 0;
    }

    //绑定地址
    //绑定地址之前需要创建协议所使用地址信息结构体
    struct sockaddr_in addr; //ipv4所用的结构体是struct sockaddr_in 
    addr.sin_family = AF_INET; //地址域信息
    addr.sin_port = htons(20001); //端口
    addr.sin_addr.s_addr = inet_addr("0.0.0.0"); //ip地址

    int ret = bind(listen_sock, (struct sockaddr*)&addr, sizeof(addr)); //addr的类型需要强转
    if(ret < 0)
    {
        perror("bind");
        return 0;
    }

    ret = listen(listen_sock, 5);
    if(ret < 0)
    {
        perror("listen");
        return 0; //监听失败，直接退出
    }

    //以上操作客户端与服务端就可以建立三次握手
    

    fd_set readfds;
    FD_ZERO(&readfds);

    FD_SET(0, &readfds);
    FD_SET(listen_sock, &readfds); //将listen_sock文件描述符添加到可读事件集合当中
    //可读事件集合当中 有两个文件描述符
    

    while(1)
    {
        int ret = select(listen_sock + 1, &readfds, NULL, NULL, NULL);
        if(ret < 0)
        {
            perror("select");
            return 0;
        }
        else if(ret == 0)
        {
            printf("select timeout\n");
            continue;
        }
        
        if(FD_ISSET(0, &readfds)) //判断0号文件描述符是否在事件集合当中
        {
            printf("0 is in readfds\n");
            char buf[1024] = {0};
            read(0, buf, sizeof(buf) - 1);
            printf("buf is : %s\n", buf);
        }
        else 
        {
            printf("0 is not in readfds\n");
        }

        if(FD_ISSET(listen_sock, &readfds))
        {
            printf("listen_sock is in readfds\n");
        }
        else 
        {
            printf("listen_sock is not in readfds\n");
        }

    }
    return 0;
}
